package esdomaci.gamemap;

import java.awt.Point;
import java.util.ArrayList;

import esdomaci.mediator.IMapaVidljivoAI;
import esdomaci.mediator.IMapaVidljivoPlatnu;
import esdomaci.mediator.IMapaVidljivoSimulatoru;
import esdomaci.mediator.ISimulatorVidljivoAI;

public class MatricaIgre implements IMapaVidljivoPlatnu, IMapaVidljivoAI, IMapaVidljivoSimulatoru {

    private Stanje[][] matrica = new Stanje[4][4];
    
    private Point kanibal;    
    private Point rupa1;
    private Point rupa2;
    private Point zlato;
    
    ISimulatorVidljivoAI owner;
    
    public MatricaIgre(ISimulatorVidljivoAI owner) {
        this.owner = owner;
        resetuj();
    }
    
    public Stanje vrati(Point l) {
        return matrica[l.x][l.y];
    }
    
    public Stanje vrati(int x, int y) {
        return matrica[x][y];
    }
    
    public void resetuj() {
    	for (int i=0; i<4; i++)
            for (int j=0; j<4; j++)
                matrica[i][j] = new Stanje();
    }
    
    public boolean getSpremnoZaRad() {
        if (kanibal == null)
            return false;
        if (rupa1 == null)
            return false;
        if (rupa2 == null)
            return false;
        if (zlato == null)
            return false;
        return true;
    }
    
    /**
     * Da li je u nekoj lokalnoj odaji KANIBAL?
     * @return Tacno ukoliko je u nekoj od odaja pored ove kanibal
     */
    public boolean osecamMirisKanibala(Point igrac) {        
        int absX = Math.abs(igrac.x - kanibal.x);
        int absY = Math.abs(igrac.y - kanibal.y);        
        return ((absX==1 && absY==0) || (absX==0 && absY==1));
    }
    
    /**
     * Da li je u nekoj lokalnoj odaji RUPA?
     * @return Tacno ukoliko je u nekoj od odaja pored ove rupa (ili dve)
     */
    public boolean osecamPromaju(Point igrac) {        
        int absX = Math.abs(igrac.x - rupa1.x);
        int absY = Math.abs(igrac.y - rupa1.y);        
        if ((absX==1 && absY==0) || (absX==0 && absY==1))
            return true;        
        absX = Math.abs(igrac.x - rupa2.x);
        absY = Math.abs(igrac.y - rupa2.y);        
        return ((absX==1 && absY==0) || (absX==0 && absY==1));
    }
    
    /**
     * Da li je u nekoj lokalnoj odaji ZLATO?
     * @return Tacno ukoliko je u nekoj od odaja pored ove zlato
     */
    public boolean osecamSjaj(Point igrac) {        
        int absX = Math.abs(igrac.x - zlato.x);
        int absY = Math.abs(igrac.y - zlato.y);        
        if ((absX==1 && absY==0) || (absX==0 && absY==1))
            return true;        
        absX = Math.abs(igrac.x - zlato.x);
        absY = Math.abs(igrac.y - zlato.y);        
        return ((absX==1 && absY==0) || (absX==0 && absY==1));
    }
    
    /**
     * Da li je doslo do kraja igre???
     * @return Ukoliko je null jos uvek nije doslo do kraja simulacije
     */
    public String krajIgre(Point igrac) {      
        if (igrac.equals(rupa1))
            return "РУПА - крај игре!";        
        if (igrac.equals(rupa2))
            return "РУПА - крај игре!";        
        if (igrac.equals(kanibal))
            return "КАНИБАЛ - крај игре!";        
        if (igrac.equals(zlato))
            return "ЗЛАТО - БРАВО!";        
        return null;
    }
    
    /**
     * Algoritam vraca sve odaje (ukoliko je to moguce) oznacene sa 'O'
     * sa naredne slike (odaja X se prosledjuje kao argument)
     *  O
     * OXO
     *  O
     * 
     * U pitanju su svi direktni susedi (dakle ne racunajuci dijagonalne) 
     * ove odaje 
     */
    public ArrayList<Point> vratiOkolneOdaje(Point odaja) {
        ArrayList<Point> odaje = new ArrayList<Point>();        
        if (odaja.x > 0)
            odaje.add(new Point(odaja.x-1, odaja.y));
        if (odaja.x < 3)
            odaje.add(new Point(odaja.x+1, odaja.y));
        if (odaja.y > 0)
            odaje.add(new Point(odaja.x, odaja.y-1));
        if (odaja.y < 3)
            odaje.add(new Point(odaja.x, odaja.y+1));        
        return odaje;
    }
    
    /**
     * Algoritam vraca sve odaje (ukoliko je to moguce) oznacene sa 'O'
     * sa naredne slike (odaja X se prosledjuje kao argument)
     *   O
     *  OYO
     * OYXYO
     *  OYO
     *   O
     * 
     * U pitanju je skup svih odaja koje mogu sadrzati sumnju na odaju X.
     * Y je vec moguce dobiti iz metode vratiOkolneOdaje
     */
    public ArrayList<Point> vratiSveOdajeKojeMoguDaSumnjajuNaOvuOdaju(Point odaja) {
        ArrayList<Point> sveOdaje = new ArrayList<Point>();
        
        for(Point okolnaOdaja : vratiOkolneOdaje(odaja))
            sveOdaje.addAll(vratiOkolneOdaje(okolnaOdaja));
        
        //jos samo da uklonimo X sa slike iz skupa odaja
        while (sveOdaje.contains(odaja))
            sveOdaje.remove(odaja);
        
        return sveOdaje;
    }
    
    
    /**
     * Vracanje svih odaja koje su do sada bile posecene.
     */
    public ArrayList<Point> vratiPoseceneOdaje() {
        ArrayList<Point> lista = new ArrayList<Point>();        
        for (int i=0; i<4; i++)
            for (int j=0; j<4; j++)
                if (vrati(i,j).isPoseceno())
                    lista.add(new Point(i,j));
        return lista;
    }
    
    /**
     * Vracanje svih sumnjivih odaja u matrici
     */
    public ArrayList<Point> vratiSumnjiveOdaje() {
        ArrayList<Point> lista = new ArrayList<Point>();
        for (int i=0; i<4; i++)
            for (int j=0; j<4; j++)
                if (vrati(i,j).isSumnjivo())
                    lista.add(new Point(i,j));
        return lista;
    }
    
    /**
     * Vracanje svih bezbednih odaja
     */
    public ArrayList<Point> vratiBezbednePoseceneOdaje() {
        ArrayList<Point> lista = new ArrayList<Point>();        
        for (int i=0; i<4; i++)
            for (int j=0; j<4; j++)
                if (vrati(i,j).getFiksnost()==Stanje.FIKSNO_BEZBEDNO)
                    if (vrati(i,j).isPoseceno())
                        lista.add(new Point(i,j));
        return lista;
    }
    
    /**
     * Vracanje svih odaja koje su bezbedne ali si oznacene
     * da ih je potrebno obici (u dva slucaja: ukoliko nisu nikada
     * ranije bile posecene ili ukoliko je AI zahtevao da se one
     * ponovo obidju)
     */
    public ArrayList<Point> vratiBezbedneNePoseceneOdaje() {
        ArrayList<Point> lista = new ArrayList<Point>();        
        for (int i=0; i<4; i++)
            for (int j=0; j<4; j++)
                if (vrati(i,j).getFiksnost()==Stanje.FIKSNO_BEZBEDNO)
                    if (!vrati(i,j).isPoseceno())
                        lista.add(new Point(i,j));
        return lista;
    }
    
    /**
     * Vracanje svih odaja koje imaju odgovarajucu sumnju
     */
    public ArrayList<Point> vratiOdajeSaSumnjom(int stanje, boolean fiksirano) {
        ArrayList<Point> lista = new ArrayList<Point>();
        for (int i=0; i<4; i++)
            for (int j=0; j<4; j++) {
                if (fiksirano) {
                    if (vrati(i,j).getSumnjivost()==stanje)
                        lista.add(new Point(i,j));   
                }
                else {
                    if (vrati(i,j).imaSumnjivost(stanje))
                        lista.add(new Point(i,j));
                }
            }
        return lista;
    }
    
    /**
     * Vracanje svih odaja koje su fiksirano prepoznate da
     * imaju neku opasnost.
     */
    public ArrayList<Point> vratiFiksneOpasneOdaje() {
        ArrayList<Point> lista = new ArrayList<Point>();        
        for (int i=0; i<4; i++)
            for (int j=0; j<4; j++)
                if (vrati(i,j).imaFiksnost(Stanje.FIKSNO_KANIBAL)
                        || (vrati(i,j).imaFiksnost(Stanje.FIKSNO_RUPA)))
                    lista.add(new Point(i,j));
        return lista;
    }
    
    public Point getKanibal() {
        return kanibal;
    }
    public void setKanibal(Point kanibal) {
        this.kanibal = kanibal;
        owner.output(" Канибал=("+(kanibal.x+1)+","+(kanibal.y+1)+")");
    }
    public Point getRupa1() {
        return rupa1;
    }
    public void setRupa1(Point rupa1) {
        this.rupa1 = rupa1;
        owner.output("  Рупа 1=("+(rupa1.x+1)+","+(rupa1.y+1)+")");
    }
    public Point getRupa2() {
        return rupa2;
    }
    public void setRupa2(Point rupa2) {
        this.rupa2 = rupa2;
        owner.output("  Рупа 2=("+(rupa2.x+1)+","+(rupa2.y+1)+")");
    }
    public Point getZlato() {
        return zlato;
    }
    public void setZlato(Point zlato) {
        this.zlato = zlato;
        owner.output("   Злато=("+(zlato.x+1)+","+(zlato.y+1)+")");
    }   
    
}